#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifndef NON_OS
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <config.h>

#ifdef FONT_FREETYPE2_SUPPORT


#include <systems.h>
#include <gapi.h>
#include <image.h>
#include <font_freetype2.h>

extern u32 xres, yres, bpp;
FT_FONT *fonts = NULL;

#define fonts_list_empty() (fonts->next == fonts)

void fonts_add_tail(FT_FONT *f)
{
    FT_FONT *tail;
    
    if(!fonts)
        return;
    
    if(fonts_list_empty()) {
        fonts->next = f;
        fonts->prev = f;
        f->next = fonts;
        f->prev = fonts;
    }
    else {
        tail = fonts->prev;
        tail->next = f;
        fonts->prev = f;
        f->next = fonts;
        f->prev = tail;
    }
}

FT_FONT *freetype2_alloc(void)
{
    FT_FONT *f = NULL;
    FT_Error error = 0;
    //DPRINTF("%s\n", __func__);
    f = (FT_FONT *)malloc(sizeof(FT_FONT));
    if(!f) {
        ERROR("Failed to allocate memory for font library\n");
        return (FT_FONT *)ERR_FT2_ALLOC_MEM;
    }

    f->lib = NULL;
    error = FT_Init_FreeType(&f->lib);
    if(error)
    {
        f->lib = NULL;
        ERROR("Failed to init library\n");
        return (FT_FONT *)ERR_FT2_INIT_LIB;
    }

    f->face = NULL;
#ifdef FONT_USE_MMAP
    f->font_fd = 0;
    f->font_size = 0;
#endif
    f->font_file = NULL;
    f->prev = f;
    f->next = f;

    if(!fonts)
        fonts = f;
        
    return f;
}

void freetype2_free(FT_FONT *f)
{
    //DPRINTF("%s\n", __func__);
    if(!f)
        return;
    
    if(f == fonts) {
        if(f->next) {
            fonts = f->next;
            f->next->prev = f->next;
        }
        else
            fonts = NULL;
    }
    else {
        if(f->prev)
            f->prev->next = f->next;
        if(f->next)
            f->next->prev = f->prev;
    }

    FT_Done_Face(f->face);
    f->face = NULL;
    FT_Done_FreeType(f->lib);
    f->lib = NULL;
#if defined(FONT_USE_MMAP) && !defined(NON_OS)
    munmap(f->font_base, f->font_size);
    close(f->font_fd);
#endif
    free(f);
}

FT_FONT *freetype2_add_face(char *font_file, int size)
{
    FT_FONT *f = NULL;
    FT_Error error = 0;
#if defined(FONT_USE_MMAP) && !defined(NON_OS)
    struct stat buf;
#endif
    
    //DPRINTF("%s\n", __func__);
    
    if(!font_file) {
        ERROR("Missing font file\n");
        return NULL;
    }
    
#ifndef NON_OS
    int fd;
    if((fd = open(font_file, O_RDONLY)) < 0) {
        ERROR("Font file does not exist\n");
        return NULL;
    }
    close(fd);
#endif

    if(!fonts) {
        f = freetype2_alloc();
        if(f < 0)
            return (FT_FONT *)f;
        f->font_file = font_file;
    }
    else {
        f = freetype2_get_face(font_file);
        if(!f) {
            f = freetype2_alloc();
            f->font_file = font_file;
            fonts_add_tail(f);
        }
    }
    
#ifdef FONT_USE_MMAP
#ifndef NON_OS
    stat(f->font_file, &buf);
    f->font_fd = fd;
    f->font_size = buf.st_size;
    f->font_base = (FT_Byte *)mmap(NULL, f->font_size, PROT_READ, MAP_PRIVATE, f->font_fd, 0);
    if(f->font_base == MAP_FAILED) {
        ERROR("Mmaping font failed!\n");
        return NULL;
    }
#else
    f->font_size = size;
    f->font_base = font_file;
#endif

    error = FT_New_Memory_Face(f->lib, f->font_base, f->font_size, 0, &f->face);
    if(error)
    {
        ERROR("Unable to create font face object! ERROR = %d", error);
        freetype2_free(f);
        return NULL;
    }
#else
    //DPRINTF("f: %x, f->lib: %x, f->face: %x, f->font_file: %s\n", f, f->lib, &f->face, f->font_file);
    f->face = NULL;
    error = FT_New_Face(f->lib, f->font_file, 0, &f->face);
    if(error)
    {
        ERROR("Unable to create font face object!");
        freetype2_free(f);
        return NULL;
    }
#endif
    error = FT_Select_Charmap(f->face, ft_encoding_unicode);
    if(error)
    {
        ERROR("Font file is not unicode encoding\n");
        freetype2_free(f);
        return NULL;
    }
    return f;
}

FT_FONT *freetype2_get_face(char *font_file)
{
    //DPRINTF("%s\n", __func__);
    FT_FONT *ptr;
    
    if(!fonts)
        return NULL;
    
    ptr = fonts;
    do {
        if(ptr->font_file == font_file) {
            return ptr; /* got it */
        }
        
        if(!(strcmp(ptr->font_file, font_file))) {
            return ptr; /* got it */
        }

        ptr = ptr->next;
    } while(ptr != fonts);
    
    return NULL; /* face not found */
}

u8 *freetype2_load_bmp(FT_FONT *ft, u32 code, u32 size, COLOR *bg, COLOR *fg)
{
    //DPRINTF("%s\n", __func__);
    int i, j;
    u8 *bmp;
    BOOLEAN is_space = false;
    BOOLEAN is_ascii = false;
    FT_Error error = 0;
    FT_UInt index;
    FT_GlyphSlot slot;
    FT_Glyph glyph;
    FT_BitmapGlyph bitmap_glyph;
    FT_Bitmap bitmap;
#if defined(CS_RGB565)
    u16 c;
#endif
    int xpos, xend, yend, xoffset, yoffset;
    int baseline;
    
    if(code < 256) {
        is_ascii = true;
    }
    
    FT_Set_Char_Size(ft->face, size << 6, 0, 0, 0);
    //error = FT_Set_Pixel_Sizes(ft->face, 0, 16 ); 
    index = FT_Get_Char_Index(ft->face, code);
    if(index == 0) {
        WARNING("Undefined character code\n");
        is_space = true;
    }

    FT_Load_Glyph(ft->face, index, FT_LOAD_RENDER);
    error = FT_Get_Glyph(ft->face->glyph, &glyph);
    if(error) {
        ERROR("Failed to get glyph\n");
        is_space = true;
    }

    FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
    bitmap_glyph = (FT_BitmapGlyph)glyph;
    bitmap = bitmap_glyph->bitmap;
    
    if(bitmap.width == 0 || bitmap.rows == 0)
        is_space = true;
  
    slot = ft->face->glyph;
  
    DPRINTF("bitmap_left: %d, bitmap_top: %d, bitmap.width: %d, bitmap.rows: %d\n", 
              slot->bitmap_left, slot->bitmap_top, bitmap.width, bitmap.rows);
    if(is_space) {
        ft->width = size/2;
        ft->height = size;
    }
    else {
        ft->width = bitmap.width;
        ft->height = size;
    }
    ft->width++;

    bmp = malloc(ft->width * ft->height * bpp);
    
    if(bmp == NULL)
    {
        ERROR("Error allocating memory for font %x.\n", code);
        return NULL;
    }

    if(!bg) bg = pCOLOR_TRANSPARENT;
    if(!fg) fg = pCOLOR_BLACK;
  
#if defined(CS_RGB565)
#if defined(NIOS2_COLOR_TRANSFORM_SUPPORT)
    c = (u16)CT_RGB2RGB565(bg->r, bg->g, bg->b);
#else
    c = RGB2PIXEL565(bg->r, bg->g, bg->b);
#endif
    for(i = 0; i < (ft->width * ft->height) / 2; i++)
        ((u32 *)bmp)[i] = (c << 16) | c;
#elif defined(CS_RGB888)
    for(i = 0; i < ft->width * ft->height * 3; i += 3) {
        bmp[i] = bg->r;
        bmp[i+1] = bg->g;
        bmp[i+2] = bg->b;
    }
#endif
  
    if(is_space) /* This char is a space or an unknown char */
        return bmp;
  
    xoffset = 0; //(ft->width - (bitmap.width + slot->bitmap_left));
    xend = xoffset + bitmap.width;
  
    baseline = ft->height/5;
    yoffset = ft->height - (baseline + slot->bitmap_top);
    yend = yoffset + bitmap.rows;

    for(i = 0; i < ft->height; ++i)
    {
        for(j = 0; j < ft->width; ++j)
        {
            if(slot->bitmap_left < 0)
                xpos = (i - yoffset) * bitmap.width + (j - xoffset) - slot->bitmap_left;
            else
                xpos = (i - yoffset) * bitmap.width + (j - xoffset);
                
            if((yoffset - i) <= 0 && i < yend && 
               (xoffset - j) <= 0 && j < xend &&
                xpos >= 0 && bitmap.buffer[xpos]) {
#if defined(CS_RGB565)  
#if defined(NIOS2_COLOR_TRANSFORM_SUPPORT)
                c = (u16)CT_RGB2RGB565(fg->r, fg->g, fg->b);
#else
                c = RGB2PIXEL565(fg->r, fg->g, fg->b);
#endif
                ((u16 *)(bmp))[i * ft->width + j] = c;
#elif defined(CS_RGB888)
                ((u8 *)(bmp))[i * ft->width + j + (j << 1)] = fg->r;
                ((u8 *)(bmp))[i * ft->width + j + 1 + (j << 1)] = fg->g;
                ((u8 *)(bmp))[i * ft->width + j + 2 + (j << 1)] = fg->b;
#endif
            }
        }
    }

    FT_Done_Glyph(glyph);
    glyph = NULL;
    
    return bmp;
}

#endif /* FONT_FREETYPE2_SUPPORT */
